#include "config.h"

#include <gtest/gtest.h>

#include <string>

using namespace cfg;

// 基础规则：required + type 检查
TEST(SchemaTest, RequiredAndType)
{
    Builder b;
    SchemaRule r1;
    r1.path = "db.host";
    r1.required = true;
    r1.expected_type = SchemaValueType::String;
    SchemaRule r2;
    r2.path = "db.port";
    r2.required = true;
    r2.expected_type = SchemaValueType::Int64;
    b.add_schema_rule(r1).add_schema_rule(r2);

    b.set("db.host", std::string("localhost"));
    // 未设置 db.port，应触发缺失
    auto fr = b.freeze();
    ASSERT_FALSE(fr.has_value());

    const auto & errs = fr.error();
    bool has_missing = false;
    for (const auto & e : errs)
    {
        if (e.code == Errc::MissingKey &&
            e.message.find("missing required key") != std::string::npos)
        {
            has_missing = true;
            break;
        }
    }
    EXPECT_TRUE(has_missing);
}

// 数值范围：minimum/maximum（包含边界）
TEST(SchemaTest, NumberRange)
{
    Builder b;
    SchemaRule r;
    r.path = "timeout";
    r.expected_type = SchemaValueType::Number;
    r.has_min = true;
    r.min_value = 1.0;
    r.has_max = true;
    r.max_value = 5.0;
    b.add_schema_rule(r);

    b.set("timeout", int64_t{6});
    auto fr = b.freeze();
    ASSERT_FALSE(fr.has_value());
    const auto & errs = fr.error();
    bool has_max = false;
    for (const auto & e : errs)
    {
        if (e.code == Errc::OutOfRange && e.message.find("value > maximum") != std::string::npos)
        {
            has_max = true;
            break;
        }
    }
    EXPECT_TRUE(has_max);
}

// 枚举与正则
TEST(SchemaTest, EnumAndRegex)
{
    Builder b;
    SchemaRule r1;
    r1.path = "mode";
    r1.enum_values = {std::string("dev"), std::string("prod"), std::string("test")};
    SchemaRule r2;
    r2.path = "email";
    r2.regex_pattern = R"(^[\w.-]+@[\w.-]+\.[A-Za-z]{2,}$)";
    b.add_schema_rule(r1).add_schema_rule(r2);

    b.set("mode", std::string("stage"));                // 不在枚举内
    b.set("email", std::string("user_at_example.com")); // 不匹配正则

    auto fr = b.freeze();
    ASSERT_FALSE(fr.has_value());
    const auto & errs = fr.error();

    bool has_enum = false, has_regex = false;
    for (const auto & e : errs)
    {
        if (e.message.find("value not in enum") != std::string::npos)
            has_enum = true;
        if (e.message.find("regex mismatch") != std::string::npos)
            has_regex = true;
    }
    EXPECT_TRUE(has_enum);
    EXPECT_TRUE(has_regex);
}

// 复杂对象路径：数组下标 + 类型错误
TEST(SchemaTest, ArrayAndObjectType)
{
    Builder b;
    // 规则：items[1].x 必须存在且是字符串
    SchemaRule r;
    r.path = "items[1].x";
    r.required = true;
    r.expected_type = SchemaValueType::String;
    b.add_schema_rule(r);

    // 写入 items[1].x 为数字，且遗漏 items[1].x 本应被必填检查捕获
    b.set("items[1].x", int64_t{123});

    auto fr = b.freeze();
    ASSERT_FALSE(fr.has_value());

    bool has_type = false;
    for (const auto & e : fr.error())
    {
        if (e.code == Errc::TypeMismatch && e.message.find("type mismatch") != std::string::npos)
        {
            has_type = true;
            break;
        }
    }
    EXPECT_TRUE(has_type);
}

// Null 类型检查：required+type=null
TEST(SchemaTest, NullRequiredAndType)
{
    Builder b;
    SchemaRule r;
    r.path = "opt";
    r.required = true;
    r.expected_type = SchemaValueType::Null;
    b.add_schema_rule(r);
    b.set("opt", std::shared_ptr<const cfg::Array>()); // 占位不当，改为显式设置 null
    // 修正：将值设置为 null
    b.set("opt", nullptr);

    auto fr = b.freeze();
    ASSERT_TRUE(fr.has_value());

    View v(*fr);
    auto node = v.lookup("opt");
    ASSERT_TRUE(node.has_value());
    EXPECT_TRUE(node->is_null());
}

// 嵌套 required：一次 freeze 聚合多条缺失错误
TEST(SchemaTest, NestedRequiredAggregated)
{
    Builder b;
    SchemaRule r1;
    r1.path = "a.b.c";
    r1.required = true; // 缺失
    SchemaRule r2;
    r2.path = "x.y[2].z";
    r2.required = true; // 缺失
    b.add_schema_rule(r1).add_schema_rule(r2);

    b.set("a.b.d", int64_t{1}); // 与 c 同层但不同键

    auto fr = b.freeze();
    ASSERT_FALSE(fr.has_value());
    const auto & errs = fr.error();
    int missing_count = 0;
    for (const auto & e : errs)
        if (e.code == Errc::MissingKey)
            ++missing_count;
    EXPECT_GE(missing_count, 2);
}

// 非法正则：应当报告 invalid regex in rule
TEST(SchemaTest, InvalidRegexPattern)
{
    Builder b;
    SchemaRule r;
    r.path = "s";
    r.regex_pattern = "["; // 非法正则
    b.add_schema_rule(r);
    b.set("s", std::string("anything"));

    auto fr = b.freeze();
    ASSERT_FALSE(fr.has_value());
    bool has_invalid_regex = false;
    for (const auto & e : fr.error())
    {
        if (e.message.find("invalid regex in rule") != std::string::npos)
        {
            has_invalid_regex = true;
            break;
        }
    }
    EXPECT_TRUE(has_invalid_regex);
}

// enum 跨类型：严格匹配类型
TEST(SchemaTest, EnumCrossTypeNotMatch)
{
    Builder b;
    SchemaRule r;
    r.path = "m";
    r.enum_values = {int64_t{1}, std::string("1")};
    b.add_schema_rule(r);

    b.set("m", int64_t{1}); // 命中 int64 选项
    auto fr_ok = b.freeze();
    ASSERT_TRUE(fr_ok.has_value());

    Builder b2;
    b2.add_schema_rule(r);
    b2.set("m", std::string("1")); // 命中 string 选项
    auto fr_ok2 = b2.freeze();
    ASSERT_TRUE(fr_ok2.has_value());

    Builder b3;
    b3.add_schema_rule(r);
    b3.set("m", double{1.0}); // 不应命中任何一项
    auto fr_bad = b3.freeze();
    ASSERT_FALSE(fr_bad.has_value());
    bool has_enum = false;
    for (const auto & e : fr_bad.error())
        if (e.message.find("value not in enum") != std::string::npos)
        {
            has_enum = true;
            break;
        }
    EXPECT_TRUE(has_enum);
}

// 数值范围边界：等于最小值/最大值应通过
TEST(SchemaTest, NumberRangeBoundaryInclusive)
{
    Builder b;
    SchemaRule r;
    r.path = "n";
    r.expected_type = SchemaValueType::Number;
    r.has_min = true;
    r.min_value = 0.0;
    r.has_max = true;
    r.max_value = 1.0;
    b.add_schema_rule(r);

    b.set("n", int64_t{0});
    auto fr1 = b.freeze();
    ASSERT_TRUE(fr1.has_value());

    Builder b2;
    b2.add_schema_rule(r);
    b2.set("n", double{1.0});
    auto fr2 = b2.freeze();
    ASSERT_TRUE(fr2.has_value());
}

// Keyed 大规模性能与正确性基准（不严格计时，仅验证结果与期望匹配）
TEST(SchemaTest, ArrayMergeKeyed_LargeScaleCorrectness)
{
    const int k_n = 2000; // 规模 2e3，保证测试快速
    Builder dst;
    dst.set_merge_strategy(MergeStrategy::Deep)
        .set_array_merge_strategy(ArrayMergeStrategy::Keyed)
        .set_array_merge_key("id");

    // 初始数组 [ {id:0, v:0}, {id:1, v:1}, ... ]
    for (int i = 0; i < k_n; ++i)
    {
        dst.set(std::string("arr[") + std::to_string(i) + "].id", int64_t{i});
        dst.set(std::string("arr[") + std::to_string(i) + "].v", int64_t{i});
    }

    // 待合并数组：覆盖偶数 id 的 v，并新增 k_n..2*k_n-1
    cfg::Object src;
    auto arr = std::make_shared<cfg::Array>();
    arr->items.reserve(k_n);
    for (int i = 0; i < k_n; ++i)
    {
        cfg::Object o;
        o.props["id"] = int64_t{i};
        o.props["v"] = int64_t{i * 10};
        arr->items.push_back(
            std::shared_ptr<const cfg::Object>(std::make_shared<cfg::Object>(std::move(o))));
    }
    for (int i = k_n; i < 2 * k_n; ++i)
    {
        cfg::Object o;
        o.props["id"] = int64_t{i};
        o.props["v"] = int64_t{i * 10};
        arr->items.push_back(
            std::shared_ptr<const cfg::Object>(std::make_shared<cfg::Object>(std::move(o))));
    }
    src.props["arr"] = std::shared_ptr<const cfg::Array>(arr);

    dst.merge_object(src);
    auto fr = dst.freeze();
    ASSERT_TRUE(fr.has_value());

    View v(*fr);
    // 检查覆盖与新增
    for (int i = 0; i < 2 * k_n; ++i)
    {
        auto val = v.lookup(std::string("arr[") + std::to_string(i) + "].v");
        ASSERT_TRUE(val.has_value());
        auto vi = val->get_int64();
        ASSERT_TRUE(vi.has_value());
        if (i < k_n)
            EXPECT_EQ(*vi, i * 10); // 覆盖
        else
            EXPECT_EQ(*vi, i * 10); // 新增
    }
}

// Null vs required 负例：键缺失与键存在为 null 的区别
TEST(SchemaTest, NullRequiredNegativeCases)
{
    // 键缺失：应触发 MissingKey
    {
        Builder b;
        SchemaRule r;
        r.path = "opt";
        r.required = true;
        r.expected_type = SchemaValueType::Null;
        b.add_schema_rule(r);
        auto fr = b.freeze();
        ASSERT_FALSE(fr.has_value());
        bool has_missing = false;
        for (const auto & e : fr.error())
            if (e.code == Errc::MissingKey)
            {
                has_missing = true;
                break;
            }
        EXPECT_TRUE(has_missing);
    }

    // 键存在为 null：应通过（既满足 required 也满足 type=null）
    {
        Builder b;
        SchemaRule r;
        r.path = "opt";
        r.required = true;
        r.expected_type = SchemaValueType::Null;
        b.add_schema_rule(r);
        b.set("opt", nullptr);
        auto fr = b.freeze();
        ASSERT_TRUE(fr.has_value());
        View v(*fr);
        auto node = v.lookup("opt");
        ASSERT_TRUE(node.has_value());
        EXPECT_TRUE(node->is_null());
    }
}

// Object/Array 类型错配负例
TEST(SchemaTest, ObjectAndArrayTypeMismatch)
{
    // 期望 object，实际为 array
    {
        Builder b;
        SchemaRule r;
        r.path = "obj";
        r.expected_type = SchemaValueType::Object;
        b.add_schema_rule(r);
        // 构造数组
        auto arr = std::make_shared<cfg::Array>();
        arr->items.push_back(int64_t{1});
        b.set("obj", std::shared_ptr<const cfg::Array>(arr));
        auto fr = b.freeze();
        ASSERT_FALSE(fr.has_value());
        bool has_type = false;
        for (const auto & e : fr.error())
            if (e.code == Errc::TypeMismatch)
            {
                has_type = true;
                break;
            }
        EXPECT_TRUE(has_type);
    }

    // 期望 array，实际为 object
    {
        Builder b;
        SchemaRule r;
        r.path = "arr";
        r.expected_type = SchemaValueType::Array;
        b.add_schema_rule(r);
        // 在键 arr 下构造对象，使得路径 "arr" 存在且类型为 Object
        b.set("arr.x", int64_t{1});
        auto fr = b.freeze();
        ASSERT_FALSE(fr.has_value());
        bool has_type = false;
        for (const auto & e : fr.error())
            if (e.code == Errc::TypeMismatch)
            {
                has_type = true;
                break;
            }
        EXPECT_TRUE(has_type);
    }
}

// 测试数据文件路径辅助：返回当前测试源文件所在目录的 data/filename
static std::string
test_data_path(const char * filename)
{
    std::string file = __FILE__;
    auto pos = file.find_last_of("/\\");
    std::string dir = (pos == std::string::npos) ? std::string(".") : file.substr(0, pos);
    return dir + std::string("/data/") + filename;
}

// 端到端：从 YAML/JSON Schema 文件加载（type/enum/pattern/minimum/maximum 组合）
//   该用例在启用 CFG_ENABLE_YAML 时运行。运行时动态生成一个 schema.yaml，包含多种约束组合，
//   并通过 Builder::load_json_schema_file 加载规则后，对一组输入进行正反例校验，覆盖
//   type/enum/pattern/min/max。
TEST(SchemaTest, LoadSchemaFromYamlAndValidate)
{
#ifndef CFG_ENABLE_YAML
    GTEST_SKIP() << "CFG_ENABLE_YAML not enabled";
#else
    // 在 tests/data 下写 schema 文件
    const std::string schema_path = test_data_path("schema_sample.yaml");
    {
        FILE * fp = std::fopen(schema_path.c_str(), "w");
        ASSERT_NE(fp, nullptr);
        // schema: 顶层 properties
        // - mode: enum [dev, prod]
        // - email: string with regex pattern
        // - timeout: number minimum=1 maximum=5
        // - retries: integer minimum=0 maximum=3
        // - opt: type null（用于与其他测试对齐）
        std::fputs("type: object\n"
                   "properties:\n"
                   "  mode:\n"
                   "    type: string\n"
                   "    enum: [dev, prod]\n"
                   "  email:\n"
                   "    type: string\n"
                   "    pattern: '^[\\\w.-]+@[\\\w.-]+\\\.[A-Za-z]{2,}$'\n"
                   "  timeout:\n"
                   "    type: number\n"
                   "    minimum: 1\n"
                   "    maximum: 5\n"
                   "  retries:\n"
                   "    type: integer\n"
                   "    minimum: 0\n"
                   "    maximum: 3\n"
                   "  opt:\n"
                   "    type: null\n"
                   "required: [mode, email, timeout]\n",
                   fp);
        std::fclose(fp);
    }

    // 正例：全部满足
    {
        Builder b;
        b.load_json_schema_file(schema_path);
        b.set("mode", std::string("dev"));
        b.set("email", std::string("user@example.com"));
        b.set("timeout", double{3.5});
        b.set("retries", int64_t{2});
        b.set("opt", nullptr);
        auto fr = b.freeze();
        ASSERT_TRUE(fr.has_value());
    }

    // 反例：枚举不匹配 + 正则不匹配 + 超出最大值
    {
        Builder b;
        b.load_json_schema_file(schema_path);
        b.set("mode", std::string("stage"));     // 不在 enum
        b.set("email", std::string("invalid@")); // 不匹配 pattern
        b.set("timeout", int64_t{10});           // 超出 maximum
        auto fr = b.freeze();
        ASSERT_FALSE(fr.has_value());
        bool has_enum = false, has_regex = false, has_max = false;
        for (const auto & e : fr.error())
        {
            if (e.message.find("value not in enum") != std::string::npos)
                has_enum = true;
            if (e.message.find("regex mismatch") != std::string::npos)
                has_regex = true;
            if (e.message.find("value > maximum") != std::string::npos)
                has_max = true;
        }
        EXPECT_TRUE(has_enum);
        EXPECT_TRUE(has_regex);
        EXPECT_TRUE(has_max);
    }

    // 边界：最小值/最大值等于边界通过，整数类型严格为 integer
    {
        Builder b;
        b.load_json_schema_file(schema_path);
        b.set("mode", std::string("prod"));
        b.set("email", std::string("a.b@c.de"));
        b.set("timeout", int64_t{1}); // 等于 minimum
        b.set("retries", int64_t{3}); // 等于 maximum
        b.set("opt", nullptr);
        auto fr = b.freeze();
        ASSERT_TRUE(fr.has_value());
    }

    // 类型错配：retries 期望 integer，给出 number；opt 期望 null，给出 string
    {
        Builder b;
        b.load_json_schema_file(schema_path);
        b.set("mode", std::string("dev"));
        b.set("email", std::string("x@y.zz"));
        b.set("timeout", double{2.0});
        b.set("retries", double{2.0});         // 类型应为 integer
        b.set("opt", std::string("not_null")); // 期望 null
        auto fr = b.freeze();
        ASSERT_FALSE(fr.has_value());
        bool has_type_mismatch = false;
        for (const auto & e : fr.error())
            if (e.code == Errc::TypeMismatch)
            {
                has_type_mismatch = true;
                break;
            }
        EXPECT_TRUE(has_type_mismatch);
    }
#endif
}